﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Linq;
using System.Diagnostics;
using System.Threading.Tasks;
using BBInterfaceNET.RuntimeTemplates;

namespace BBInterfaceNET.Services
{
    public class CodeGenerationService:ICodeGenerationService
    {
        public void GenerateCode(string projectPath, string projectName, string outputDir, Action callback)
        {
            Task.Factory.StartNew(() => {
                string path = Path.Combine(projectPath, projectName);
                //relative file paths
                List<string> files = GetProjectFiles(path);
                int i = 0;
                bool genList=false;
                foreach (string file in files)
                {
                    //if file does not exist on disk continue
                    //designer file
                    FileInfo fi = new FileInfo(Path.Combine(projectPath, file));
                    if (!fi.Exists) continue;

                    //get the view and view controller file names
                    string viewName = Path.GetFileNameWithoutExtension(file);

                    if (!viewName.ToLower().EndsWith("view"))
                    {
                        viewName += "View";
                    }
                    string vcName = viewName + "Controller";
                    viewName = Path.ChangeExtension(viewName, ".java");
                    vcName = Path.ChangeExtension(vcName, ".java");

                    
                    //get the generated file path
                    string newFileName = Path.ChangeExtension(file, ".java");
                    FileInfo genFI = new FileInfo(Path.Combine(outputDir,"view", newFileName));
                    //if the view file does not exist or is out of date regenerate it
                    if (!(genFI.Exists && fi.LastWriteTime < genFI.LastWriteTime))
                        GenerateCode(file, projectPath, outputDir + "\\view", ref genList);
                    //if the view controller file does not exist generate it
                    genFI = new FileInfo(Path.Combine(outputDir, "controller", vcName));
                    //if the view file does not exist or is out of date regenerate it
                    if (!(genFI.Exists && fi.LastWriteTime < genFI.LastWriteTime))
                        GenerateViewController(outputDir, Path.Combine(Path.GetDirectoryName(file),viewName), file);

                    float percent = (float)(i++) / files.Count;
                    //notify
                }
                if (genList)
                {
                    //generate the model list item files
                    GenerateListItems(outputDir);
                    GenerateCustomListField(outputDir);
                }
                //task ended
                callback();
            });
            
        }

        private void GenerateViewController(string outputDir, string viewName, string sourceFile)
        {
            string dirPath = Path.Combine(outputDir, "controller", Path.GetDirectoryName(viewName));
            if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
            string path = Path.Combine(dirPath, Path.GetFileNameWithoutExtension(viewName) + "Controller" + Path.GetExtension(viewName));
            try
            {
                using (FileStream fs = new FileStream(path, FileMode.Create))
                {
                    using (StreamWriter sw = new StreamWriter(fs))
                    {
                        ViewControllerTemplate template = new ViewControllerTemplate();
                        template.RelativeViewPath = viewName;
                        string str = template.TransformText();
                        sw.Write(str);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(string.Format("Error generating file controller: {0}", ex.Message));
            }
        }

        private void GenerateCustomListField(string outputDir)
        {
            string dirPath = Path.Combine(outputDir, "view", "customComponents");
            if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
            string path = Path.Combine(dirPath, "CustomListField.java");
            if (File.Exists(path)) return;
            try
            {
                using (FileStream fs = new FileStream(path, FileMode.Create))
                {
                    using (StreamWriter sw = new StreamWriter(fs))
                    {
                        CustomListTemplate template = new CustomListTemplate();
                        string str = template.TransformText();
                        sw.Write(str);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(string.Format("Error generating file CustomListField: {0}", ex.Message));
            }
        }

        private void GenerateListItems(string outputDir)
        {
            string[] fileNames = new string[]{ "ListItem", "ImageTitleListItem", "SubtitleListItem",
                "ImageSubtitleListItem", "RightImageSubtitleListItem", "RightImageTitleListItem"};
            
            foreach (string s in fileNames)
            {
                ListItemTemplate template = new ListItemTemplate();
                template.FileName=s;
                string dirPath = Path.Combine(outputDir, "model", "customListItems");
                if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
                string path = Path.Combine(dirPath, s+".java");
                if (File.Exists(path)) continue;
                try
                {
                    using (FileStream fs = new FileStream(path, FileMode.Create))
                    {
                        using (StreamWriter sw = new StreamWriter(fs))
                        {
                            string str = template.TransformText();
                            sw.Write(str);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(string.Format("Error generating file {0}: {1}", s, ex.Message));
                }
            }
        }

        private void GenerateCode(string file, string projPath, string outputPath, ref bool generateList)
        {
            string absFilePath=Path.Combine(outputPath, file);
            string viewName=Path.GetFileNameWithoutExtension(file);
            if (!viewName.ToLower().EndsWith("view"))
                viewName += "View";
            absFilePath = Path.Combine(Path.GetDirectoryName(absFilePath), viewName);
            absFilePath = Path.ChangeExtension(absFilePath, ".java");
            string absPath = Path.GetDirectoryName(absFilePath);

            if (!Directory.Exists(absPath))
                Directory.CreateDirectory(absPath);

            Debug.WriteLine("Generating code for " + file + " ...");
            if (File.Exists(absFilePath))
            {
                Debug.WriteLine(string.Format("Deleting old {0} ...", absFilePath));
                File.Delete(absFilePath);
            }
            Debug.WriteLine(string.Format("Creating new {0} ...", absFilePath));
            using (FileStream fs = File.Create(absFilePath)) { }

            TranslateFile(Path.Combine(projPath, file), absFilePath, ref generateList);
        }
        private void TranslateFile(string screenDefinitionFile, string outputFile, ref bool generateList)
        {
            FieldGenerationTemplate gen = new FieldGenerationTemplate();
            gen.SourceFilePath = screenDefinitionFile;
            gen.OutputPath = outputFile;
            string res = gen.TransformText();
            if (res.Contains("CustomListField"))
                generateList = true;
            try
            {
                using (FileStream fs = new FileStream(outputFile, FileMode.OpenOrCreate))
                {
                    fs.SetLength(0);
                    using (StreamWriter sw = new StreamWriter(fs))
                    {
                        sw.Write(res);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(string.Format("error writing generated file for {0}: {1}", outputFile, ex.Message));
            }
        }
        private List<string> GetProjectFiles(string path)
        {
            string root = Path.GetDirectoryName(path);

            List<string> files = new List<string>();
            XDocument doc = XDocument.Load(path);
            foreach (var el in doc.Descendants("file"))
            {
                var attr = el.Attributes("name").FirstOrDefault();

                if (attr == null) continue;
                if (!attr.Value.EndsWith("bbfx")) continue;

                files.Add(attr.Value.Substring(1));
            }

            return files;
        }
    }
}
